﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace dotNet
{
    public class Net
    {

        private ReceiveCallback callback = null;
        private Configuration conf = null;
        private Socket sck = null;
        private TcpListener svr = null;
        private TcpClient tcpclnt = null;
        private Receiver receiver = null;
        private bool running = true;
        private List<Receiver> receiverList = null;
        public Stream stream = null;

        public Net()
        {
        }

        public void initialize(Configuration conf)
        {
            this.conf = conf;

            if (conf.transport == Configuration.TRANSPORT_TCP) {
                if (conf.mode == Configuration.MODE_SENDER) {
                    tcpclnt.Connect(conf.ip, conf.port);
                    stream = tcpclnt.GetStream();
                } else if (conf.mode == Configuration.MODE_RECEIVER) {
                    try {
                        receiverList = new List<Receiver>();
                        IPAddress ipAd = IPAddress.Parse(conf.ip);
                        svr = new TcpListener(ipAd, conf.port);
                    } catch (IOException ioe) { }
                }
            } else { }
        }

        public void start()
        {
            if (conf.mode == Configuration.MODE_SENDER) {
                if (this.callback != null) {
                    receiver = new Receiver(tcpclnt, this.callback);
                    receiver.start();
                }
            } else if (conf.mode == Configuration.MODE_RECEIVER) {
                svr.Start();
            }
        }

        public void loop()
        {
            if (conf.mode == Configuration.MODE_SENDER)
            {
                while (running)
                {
                    Thread.Sleep(1000);
                }
            }
            else if (conf.mode == Configuration.MODE_RECEIVER)
            {
                while (running)
                {
                    try
                    {
                        TcpClient tcpClient = svr.AcceptTcpClient();
                        this.tcpclnt = tcpClient;
                        Console.WriteLine("Accept Client !!");
                        if (this.callback != null)
                        {
                            receiver = new Receiver(tcpClient, this.callback);
                            receiver.start();
                            receiverList.Add(receiver);
                        }
                    }
                    catch (IOException ioe)
                    {
                    }
                    catch (SocketException se)
                    { }
                }
            }
        }

        public void shutdown()
        {
            try
            {
                running = false;
                if (receiver != null)
                    receiver.shutdown();
                while (receiverList.Count > 0)
                {
                    Receiver recv = receiverList.First();
                    receiverList.RemoveAt(0);
                    recv.shutdown();
                }
                if (svr != null)
                    svr.Stop();
                if (tcpclnt != null)
                    tcpclnt.Close();
            }
            catch (IOException e) { }
        }

        public List<Receiver> getReceiverList()
        {
            return receiverList;
        }

        public void send(Msg msg)
        {
            if (conf.mode == Configuration.MODE_SENDER) {
	            byte[] buffer = msg.packet.toByteArray();           
	            stream.Write(buffer, 0, buffer.Length);
	            stream.Flush();
    	    } else if (conf.mode == Configuration.MODE_RECEIVER) { // BC : call net.send(msg);
    		    foreach (Receiver receiver in receiverList) {
    			    receiver.send(msg);
    		    }
    	    }
        }

        public void receiveCallback(ReceiveCallback callback)
        {
            this.callback = callback;
        }
    }
}
